package msu.ml.gis;

import java.util.*;
import java.awt.geom.Rectangle2D;

/**
 * The Shapefile class represents a GIS shapefile.
 *
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class Shapefile 
{
	public static final int FILE_CODE = 9994;
	public static final int VERSION = 1000;
	public static final double LATITUDE_DEGREE_SIZE = 111000;

	private double lat, lon;
	private int shapeType;
	private ArrayList<IShape> m_IShape;
   private ArrayList<DBFAttributeHeader> headers;

	/**
	 * Create a new shapefile
	 */
	public Shapefile()
	{
		m_IShape = new ArrayList<IShape>();
      headers = new ArrayList<DBFAttributeHeader>();
	}

	/**
	 * Create a new shapefile centered at the
	 * specified latitude and longitude.
	 */
	public Shapefile(double lat, double lon)
	{
		this();
		this.lat = lat;
		this.lon = lon;
	}

	/**
	 * Get the latitude for this shapefile
	 *
	 * @return the latitude for this shapefile
	 */
	public double getLatitude() 
	{ 
		return this.lat; 
	}

	/**
	 * Get the longitude for this shapefile
	 *
	 * @return the longitude for this shapefile
	 */
	public double getLongitude() 
	{ 
		return this.lon; 
	}

	/**
	 * Get the longitude degree size in meters 
	 * at the specified latitude.
	 *
	 * @param lat Latitude
	 * @return the longitude degree size at the specified latitude
	 */
	public static double getLongitudeDegreeSize(double lat)
	{
		return LATITUDE_DEGREE_SIZE * Math.cos(Math.toRadians(lat));
	}

	/**
	 * Translates the specified point to be specified at the 
	 * latitude and longitude for this shapefile.
	 *
	 * @param p original point
	 * @return the translated point
	 */
	public Point getGeographicalPoint(Point p)
	{
		double newLat = this.lat + (p.getY() / LATITUDE_DEGREE_SIZE);
		double newLon = this.lon + (p.getX() / getLongitudeDegreeSize(this.lat));
		return new Point(newLon, newLat);
	}

	/**
	 * Add a shape to this shapefile
	 *
	 * @param shape the shape to add
	 */
	public void addShape(IShape shape)
	{
		m_IShape.add(shape);
	}

   public void addHeader(DBFAttributeHeader header)
   {
      headers.add(header);
   }

	/**
	 * Get the Bounding Box for the shapes
	 * in this shapefile
	 *
	 * @return the bounding box for this shapefile
	 */
	public Rectangle2D getBoundingBox()
	{
		if(m_IShape.size() == 0)
		  return null;


		Rectangle2D result = m_IShape.get(0).getBoundingBox();
		for(int i = 1; i < m_IShape.size(); i++)
			result = result.createUnion(m_IShape.get(i).getBoundingBox());

		return result;
	}

	/**
	 * Get the collection of shapes in this shapefile
	 *
	 * @return the collection of shapes in this shapefile
	 */
	public ArrayList<IShape> getShapes()
	{
		return m_IShape;
	}

   public ArrayList<DBFAttributeHeader> getHeaders()
   {
      return this.headers;
   }

	/**
	 * Get the number of shapes in this shapefile
	 *
	 * @return the number of shapes in this shapefile
	 */
	public int getNumShapes()
	{
		return m_IShape.size();
	}

	/**
	 * Get the shape type for this shapefile. All Shapes
	 * in a shapefile must be of the same type.
	 *
	 * @return the shape type for this shapefile
	 */
	public int getShapeType()
	{
		return shapeType;
	}

	/**
	 * Get the size of this shapefile in 16bit words
	 *
	 * @return the size of this shapefile in 16bit words
	 */
	public int getFileSize()
	{
		int fileSize = 50;
		for(int i = 0; i < m_IShape.size(); i++)
			fileSize += (4 + m_IShape.get(i).getContentLength());
		return fileSize;
	}

	/**
	 * Set the shape type for this shapefile
	 * 
	 * @param type the shape type
	 */
	public void setShapeType(int type)
	{
		shapeType = type;
	}

}
